# -*- coding: utf-8 -*-
import hashlib
import json
import time
from collections import OrderedDict

import requests
import xmltodict
from django.conf import settings

from async import async_job
from common.agentpay.base import AbstractHandler
from common.agentpay.db import get_agent_pay_order
from common.agentpay.model import AGENT_PAY_STATUS
from common.cache import redis_cache
from common.channel import admin_db as channel_admin_db
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError
from common.utils.types import Enum
from common.utils.tz import local_now

_LOGGER = track_logging.getLogger(__name__)

APP_CONF = {
    # 这些是之前的，现在不用了，先注释掉
    # '210001110013687': {  # dwc 支付宝3.5%（1---500）
    #     'API_KEY': 'ca4319fb9678bf87a848aec16dd3fa59',
    #     'gateway': 'http://trade.cd178.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110013676': {  # dwc 支付宝3.5%（10---2000） 北京鸿创远大商贸有限公司
    #     'API_KEY': '6a043f684b3572169836192d843a919f',
    #     'gateway': 'http://trade.cd178.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110013509': {  # 北京鸿创远大商贸有限公司  支付宝H5
    #     'API_KEY': '7e8d16b6363f1a0b2fa8308a3bffe89e',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001350013517': {  # witch 支付宝3.3%（10---3000）D0结算
    #     'API_KEY': '79c6898b8216c91f29c7f089edeb97b7',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001410013530': {  # witch 支付宝3.3%（10---3000）D0结算
    #     'API_KEY': 'b944dfaa0d343234551690681d108450',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001410013880': {  # witch  定额支付宝10，20，30，40，50，60，70，80，90，100，200，300，400，500，600，700，800，900，1000
    #     'API_KEY': '9cf26a3cdaed81bce0b821aae2a2f0cd',
    #     'gateway': 'http://trade.cd178.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001410013881': {  # witch 支付宝3.3%（10---2000）D0结算
    #     'API_KEY': '38a1102eb9b21d80eb5a1e9acceb8803',
    #     'gateway': 'http://trade.cd178.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001410013605': {  # witch
    #     'API_KEY': '19796d6890fa7d5811f52c422bc7c0d8',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110013604': {  # ks
    #     'API_KEY': 'faf87890126c0d93517c5cb60fdceffa',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001330013636': {  # dwc 1000 支付宝
    #     'API_KEY': '9c80a15c882a1f63a3a3d8b0a78b1f5d',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001330014159': {  # dwc 2000 支付宝
    #     'API_KEY': 'ee13bd5e3c5eb2a73b6fc33f31bcef4f',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001330014139': {  # dwc 5000 支付宝
    #     'API_KEY': '573fa881c5ea91a6dd56c9773c3dd92b',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110014180': {  # witch 1000 支付宝
    #     'API_KEY': '0746a1f0fd3a3f452afd549f110ded3e',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110014181': {  # witch 2000 支付宝
    #     'API_KEY': '5154808d024e1e65ec016d62ce423b64',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110013637': {  # witch 5000 支付宝
    #     'API_KEY': '1692cea6afb271642c05f630833a871e',
    #     'gateway': 'http://trade.id888.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110014228': {  # witch 10-5000 快捷
    #     'API_KEY': 'a2227807e633a2bac8d4f6b36c9bb7c4',
    #     'gateway': 'http://trade.cd178.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110013824': {  # 迅联宝 支付宝wap 3% 100-5000 tt
    #     'API_KEY': '2c1b558e675dc58e2ebbe08e655c9732',
    #     'gateway': 'http://trade.cd178.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001330014578': {  # ks 快捷：1.4（10----5000）
    #     'API_KEY': '0622405286cab65eb5855a76c8f821a7',
    #     'gateway': 'http://trade.cd178.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    # '210001110014736': {  # tt 微信H5：3.5% 20、30、50、100、200定额
    #     'API_KEY': '8004cd56b16f5f624b7eb3f1ea271f0a',
    #     'gateway': 'http://trade.cd178.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    # },
    '210001110013509': {  # 凯撒的固定金额微信 （20，30，50，100）
        'API_KEY': '12f0e5e3d60a3f533bca9f78d1e5f8d2',
        'gateway': 'http://trade.rich1994.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    },
    '210001110013510': {  # witch的固定金额微信 （20，30，50，100）
        'API_KEY': '7a4e40c495deb483f50def2c132c6aaa',
        'gateway': 'http://trade.rich1994.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    },
    '210001110013537': {  # witch的支付宝
        'API_KEY': '525ee521ed028882665e530dd74e05b3',
        'gateway': 'http://trade.rich1994.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    },
    '210001110013512': {  # 凯撒的支付宝 充值单笔（50---5000）
        'API_KEY': '7efa15546befe773690a05897f0fbb73',
        'gateway': 'http://trade.rich1994.cn:8880/cgi-bin/netpayment/pay_gate.cgi',
    },
}


def _get_api_key(mch_id):
    return APP_CONF[mch_id]['API_KEY']


def _get_gateway(mch_id):
    return APP_CONF[mch_id]['gateway']


# 微信H5 WXWAP 支付宝H5 ZFBWAP
# QQ钱包H5 QQWAP
def _get_pay_type(service):
    if service == 'alipay':
        return 'ZFBWAP'
    elif service == 'wxpay':
        return 'WXWAP'
    elif service == 'qq':
        return 'QQWAP'
    elif service == 'quick':
        return 'UNIONWAP'
    else:
        return 'QQWAP'  # jd


def generate_sign(d, key):
    '''  生成下单签名 '''
    s = 'apiName={}&apiVersion={}&platformID={}&merchNo={}&orderNo={}&tradeDate={}&amt={}&merchUrl={}&merchParam={}&tradeSummary={}{}'.format(
        d['apiName'], d['apiVersion'], d['platformID'], d['merchNo'], d['orderNo'], d['tradeDate'], d['amt'],
        d['merchUrl'], d['merchParam'], d['tradeSummary'], key
    )
    _LOGGER.info('xlbpay generate_sign s: %s', s)
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().upper()
    return sign


def generate_notify_sign(d, key):
    '''  生成下单签名 '''
    s = 'apiName={}&notifyTime={}&tradeAmt={}&merchNo={}&merchParam={}&orderNo={}&tradeDate={}&accNo={}&accDate={}&orderStatus={}{}'.format(
        d['apiName'], d['notifyTime'], d['tradeAmt'], d['merchNo'], d['merchParam'], d['orderNo'], d['tradeDate'],
        d['accNo'], d['accDate'], d['orderStatus'], key
    )
    _LOGGER.info('xlbpay generate_notify_sign s: %s', s)
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().upper()
    return sign


# "apiName=%s&apiVersion=%s&platformID=%s&merchNo=%s&orderNo=%s&tradeDate=%s&amt=%s"
def generate_query_sign(d, key):
    '''  生成下单签名 '''
    s = 'apiName={}&apiVersion={}&platformID={}&merchNo={}&orderNo={}&tradeDate={}&amt={}{}'.format(
        d['apiName'], d['apiVersion'], d['platformID'], d['merchNo'], d['orderNo'], d['tradeDate'], d['amt'], key
    )
    _LOGGER.info('xlbpay generate_query_sign s: %s', s)
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().upper()
    return sign


def generate_draw_sign(d, key):
    '''  生成下单签名 '''
    s = 'apiName={}&apiVersion={}&platformID={}&merchNo={}&orderNo={}&tradeDate={}&merchUrl={}&merchParam={' \
        '}&bankAccNo={}&bankAccName={}&bankCode={}&bankName={}&amt={}&tradeSummary={}{}'.format(
        d['apiName'], d['apiVersion'], d['platformID'], d['merchNo'], d['orderNo'], d['tradeDate'], d['merchUrl'],
        d['merchParam'], d['bankAccNo'], d['bankAccName'], d['bankCode'], d['bankName'], d['amt'], d['tradeSummary'],
        key
    )
    _LOGGER.info('xlbpay generate_draw_sign s: %s', s)
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().upper()
    return sign


def generate_draw_query_sign(d, key):
    '''  生成下单签名 '''
    s = 'apiName={}&apiVersion={}&platformID={}&merchNo={}&orderNo={}&tradeDate={}{}'.format(
        d['apiName'], d['apiVersion'], d['platformID'], d['merchNo'], d['orderNo'], d['tradeDate'], key
    )
    _LOGGER.info('xlbpay generate_draw_query_sign s: %s', s)
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().upper()
    return sign


def generate_draw_notify_sign(d, key):
    '''  生成下单签名 '''
    s = 'apiName={}&merchNo={}&orderNo={}&tradeDate={}&merchParam={}&tradeAmt={}&orderStatus={}&settTime={}&batchNo={}&accDate={}{}'.format(
        d['apiName'], d['merchNo'], d['orderNo'], d['tradeDate'], d['merchParam'], d['tradeAmt'], d['orderStatus'],
        d['settTime'], d['batchNo'], d['accDate'], key
    )
    _LOGGER.info('xlbpay generate_draw_notify_sign s: %s', s)
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().upper()
    return sign


def verify_notify_sign(params, key):
    sign = params['signMsg']
    params.pop('signMsg')
    calculated_sign = generate_notify_sign(params, key)
    if sign != calculated_sign:
        _LOGGER.info("xlbpay sign: %s, calculated sign: %s", sign, calculated_sign)
        raise ParamError('sign not pass, data: %s' % params)


def verify_draw_notify_sign(params, key):
    sign = params['signMsg']
    params.pop('signMsg')
    calculated_sign = generate_draw_notify_sign(params, key)
    if sign != calculated_sign:
        _LOGGER.info("xlbpay sign: %s, calculated sign: %s", sign, calculated_sign)
        raise ParamError('sign not pass, data: %s' % params)


def _build_form(params, gateway):
    html = u"<head><title>loading...</title></head><form id='submit' name='submit' action='" + gateway + "' method='post'>"
    for k, v in params.items():
        html += "<input type='hidden' name='%s' value='%s'/>" % (k, v)
    html += "</form>"
    html += "<script>doc" \
            "ument.forms['submit'].submit();</script>"
    return html


def create_charge(pay, pay_amount, info):
    ''' 创建订单 '''
    app_id = info['app_id']
    api_key = _get_api_key(app_id)
    service = info.get('service')
    parameter_dict = OrderedDict((
        ('apiName', 'WAP_PAY_B2C'),
        ('apiVersion', '1.0.0.0'),
        ('platformID', app_id),
        ('merchNo', app_id),
        ('orderNo', str(pay.id)),
        ('tradeDate', time.strftime("%Y%m%d", time.localtime())),
        ('amt', '%.2f' % pay_amount),
        ('merchUrl', '{}/pay/api/{}/xlbpay/{}'.format(settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id)),
        ('merchParam', 'charge_body'),
        ('tradeSummary', 'charge'),
        ('bankCode', _get_pay_type(service)),
    ))
    parameter_dict['signMsg'] = generate_sign(parameter_dict, api_key)
    html_text = _build_form(parameter_dict, _get_gateway(app_id))
    cache_id = redis_cache.save_html(pay.id, html_text)
    url = settings.PAY_CACHE_URL + cache_id
    _LOGGER.info("xlbpay create  data: %s, %s", json.dumps(parameter_dict), url)
    return {'charge_info': url}


# SUCCESS
def check_notify_sign(request, app_id):
    data = dict(request.POST.iteritems())
    api_key = _get_api_key(app_id)
    _LOGGER.info("xlbpay notify data: %s", data)
    _LOGGER.info("xlbpay notify body: %s", request.body)
    verify_notify_sign(data, api_key)
    pay_id = data['orderNo']
    if not pay_id:
        _LOGGER.error("fatal error, out_trade_no not exists, data: %s" % data)
        raise ParamError('xlbpay event does not contain pay ID')

    pay = order_db.get_pay(int(pay_id))
    if not pay:
        raise ParamError('pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('pay %s has been processed' % pay_id)

    mch_id = pay.mch_id
    trade_status = str(data['orderStatus'])
    trade_no = data['accNo']
    total_fee = float(data['tradeAmt'])
    extend = {
        'trade_status': trade_status,
        'trade_no': trade_no,
        'total_fee': total_fee
    }

    from common.channel.pay import check_channel_order
    check_channel_order(pay_id, total_fee, app_id)
    # 0未支付，1成功，2失败
    if trade_status == '1':
        _LOGGER.info('xlbpay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
        # async notify
        async_job.notify_mch(pay_id)


def query_charge(pay_order, app_id):
    ''' 查询订单 '''
    # prepare param
    pay_id = pay_order.id
    api_key = _get_api_key(app_id)
    parameter_dict = OrderedDict((
        ('apiName', 'MOBO_TRAN_QUERY'),
        ('apiVersion', '1.0.0.0'),
        ('platformID', app_id),
        ('merchNo', app_id),
        ('orderNo', str(pay_id)),
        ('tradeDate', pay_order.created_at.strftime("%Y%m%d")),
        ('amt', '%.2f' % pay_order.total_fee),
    ))
    parameter_dict['signMsg'] = generate_query_sign(parameter_dict, api_key)
    _LOGGER.info("xlbpay query  data: %s, order_id is: %s", json.dumps(parameter_dict), parameter_dict['orderNo'])
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_gateway(app_id), data=parameter_dict, headers=headers, timeout=10)
    _LOGGER.info("xlbpay query  rsp data: %s", response.text)
    if response.status_code == 200:
        data = xmltodict.parse(response.text)['moboAccount']['respData']
        trade_status = str(data['Status'])
        trade_no = data['accNo']
        total_fee = float(pay_order.total_fee)

        extend = {
            'trade_status': trade_status,
            'trade_no': trade_no,
            'total_fee': total_fee
        }

        # 1.表示成功
        # 2.表示失败
        # 4.表示交易中
        if trade_status == '1':
            _LOGGER.info('xlbpay query order success, user_id:%s pay_id:%s' % (pay_order.mch_id, pay_id))
            res = order_db.add_pay_success(pay_order.mch_id, pay_order.id,
                                           total_fee, trade_no, extend)
            if res:
                # async notify
                async_job.notify_mch(pay_order.id)
            print("order process successfully. %s" % pay_order.id)
    else:
        _LOGGER.warn('xlbpay data error, status_code: %s', response.status_code)


def _get_bank_type(bank_type):
    if bank_type == 'ICBC':
        return 'ICBC'
    elif bank_type == 'CCB':
        return 'CCB'
    elif bank_type == 'PNB':
        return 'PAB'
    elif bank_type == 'BOC':
        return 'BOC'
    elif bank_type == 'ABC':
        return 'ABC'
    elif bank_type == 'BCM':
        return 'COMM'
    elif bank_type == 'CMB':
        return 'CMB'
    elif bank_type == 'CNCB':
        return 'CITIC'
    elif bank_type == 'CEB':
        return 'CEB'
    elif bank_type == 'HXB':
        return 'HXB'
    elif bank_type == 'SPDB':
        return 'SPDB'
    elif bank_type == 'CIB':
        return 'CIB'
    elif bank_type == 'CMBC':
        return 'CMBC'
    elif bank_type == 'CGB':
        return 'CGB'
    elif bank_type == 'PSBC':
        return 'PSBC'
    else:
        return 'THIRD_NOT_SUPPORT'


NOTIFY_PAY_STATUS = Enum({
    "WAIT": (0, u"等待"),
    "SUCCESS": (1, u"成功"),
    "FAIL": (2, u"失败"),
})


def convert_notify_status(status):
    if status == NOTIFY_PAY_STATUS.SUCCESS:
        return AGENT_PAY_STATUS.SUCCESS
    elif status == NOTIFY_PAY_STATUS.FAIL:
        return AGENT_PAY_STATUS.FAIL
    elif status == NOTIFY_PAY_STATUS.WAIT:
        return AGENT_PAY_STATUS.SUBMIT


class xlbPayHandler(AbstractHandler):
    def submit_order(self, order_no):
        """
        创建代付请求
        :return:
        """
        agent_pay = get_agent_pay_order(order_no)
        chn = channel_admin_db.get_channel(int(agent_pay.channel_id))

        chn_info = json.loads(chn.info)
        app_id = chn_info['app_id']
        params = {
            'apiName': 'SINGLE_ENTRUST_SETT',
            'apiVersion': '1.0.0.0',
            'platformID': app_id,
            'merchNo': app_id,
            'orderNo': agent_pay.order_no,
            'tradeDate': local_now().strftime('%Y%m%d'),
            'merchUrl': '{}/pay/api/{}/xlbpay/'.format(settings.NOTIFY_PREFIX, settings.AGENT_PAY_NOTIFY_PATH),
            'merchParam': '',
            'bankAccNo': agent_pay.card_no,
            'bankAccName': agent_pay.account_name,
            'bankCode': _get_bank_type(agent_pay.bank_code),
            'bankName': agent_pay.bank_name.encode('utf-8'),
            'amt': '%.2f' % agent_pay.total_fee,
            'tradeSummary': 'agentpay',
            'phone': agent_pay.phone_number,
            'idcard': agent_pay.identity_card,
            'province': agent_pay.account_province.encode('utf-8'),
            'city': agent_pay.account_city.encode('utf-8'),
        }
        params['signMsg'] = generate_draw_sign(params, _get_api_key(app_id))
        _LOGGER.info('xlbpay draw data: %s', json.dumps(params))
        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        response = requests.post(_get_gateway(app_id), data=params, timeout=3, headers=headers, verify=False)
        _LOGGER.info("xlbpay draw rsp data: %s %s", response.status_code, response.text)
        data = xmltodict.parse(response.text)['moboAccount']['respData']

        if data['respCode'] == '00':
            return params['orderNo'], '', '申请已受理->' + str(response.text)
        else:
            return params['orderNo'], '', '未知状态->' + str(response.text)

    def agent_pay_notify(self, order_no, status, pay_fee):
        """
         代付到账回调
         :return:
         """
        _LOGGER.info("status a is: %s", status)
        order = get_agent_pay_order(order_no)
        assert int(status) in (NOTIFY_PAY_STATUS.SUCCESS, NOTIFY_PAY_STATUS.FAIL)
        status = convert_notify_status(int(status))
        self.agent_pay_callback(order_no, status)

    def query_agent_pay(self, order_no):
        """
        查询代付请求
        :return:
        """
        agent_pay = get_agent_pay_order(order_no)
        chn = channel_admin_db.get_channel(int(agent_pay.channel_id))
        chn_info = json.loads(chn.info)
        app_id = chn_info['app_id']
        params = {
            'apiName': 'SINGLE_SETT_QUERY',
            'apiVersion': '1.0.0.0',
            'platformID': app_id,
            'merchNo': app_id,
            'orderNo': agent_pay.order_no,
            'tradeDate': local_now().strftime('%Y%m%d'),
        }
        params['signMsg'] = generate_draw_query_sign(params, _get_api_key(app_id))
        _LOGGER.info('xlbpay draw query data: %s', json.dumps(params))
        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        response = requests.post(_get_gateway(app_id), data=params, timeout=3, headers=headers, verify=False)
        _LOGGER.info("xlbpay draw query rsp data: %s %s", response.status_code, response.text)
        if response.status_code != 200:
            raise ParamError('xlbpay query_agent_pay error')
        data = xmltodict.parse(response.text)['moboAccount']['respData']
        # 委托结算状态
        # 0-已受理，1-成功，2-失败，5-处理中
        if data['Status'] == '0':
            results = '已受理->' + str(response.text)
        elif data['Status'] == '1':
            results = '成功->' + str(response.text)
        elif data['Status'] == '2':
            results = '失败->' + str(response.text)
        elif data['Status'] == '5':
            results = '处理中->' + str(response.text)
        else:
            results = '未知状态->' + str(response.text)
        return results

    def query_balance(self, info):
        return u'该渠道没有余额查询接口'


handler = xlbPayHandler()
